<?php
namespace lib;

use think\Exception;
use think\Log;

class HnnxClient
{
    //私钥文件路径
    private $rsaPrivateKeyFilePath = '';

    //私钥文件密码
    private $pemPass = '';

    //私钥值
    private $rsaPrivateKey;//与私钥文件二选一

    //交易编码:UTF-8
    private $charset = "";

    //开发者申请appCode的，需要开发平台提供给开发者
    private $appCode = '';

    //接口的版本
    private $version = '';

    //交易渠道号:45
    private $channelId = '';

    //加密算法
    private $algorithm = '';

    //设备编号
    private $deviceId = '';
    //平台商户号 一级商户号
    private $MerNbr = '';

    //交易机构号
    private $branchId = '';

    //公钥
    private $rsaPublicKeyPath = '';

    //网关
    private $gatewayUrl = "";

    //二级商户号
    private $SubMerchantId = "";
    //平台商户号
    private $MerplatFormNbr = "";
    //平台机构号
    private $PlatformDept = "";
    
    private $SubAppid = "";


    public function __construct($config = [])
    {

        $this->appCode               = '20250617001';//开发者appCode
        $this->rsaPrivateKeyFilePath =  '/www/wwwroot/car_parts/cert/xya_private.pem';//私钥文件
        $this->rsaPublicKeyPath      =  '/www/wwwroot/car_parts/cert/openbank.cer';//公钥文件
        $this->MerNbr                = '010020170801000001';
        $this->version               = '1.0';
        $this->channelId             = '45';
        $this->deviceId              = '00000302QLNL00044389';
        $this->charset               = 'UTF-8';
        $this->algorithm             = 'SHA256withRSA';
        $this->gatewayUrl            = 'https://open.hnnx.com:8019/SericeCenter';
        $this->pemPass               = '';
        $this->rsaPrivateKey         = '';
        $this->MerplatFormNbr        = '';
        $this->PlatformDept          = '';
        $this->SubAppid              = 'wxee9d34876eea271e';

        $this->branchId              = $config['brandId']??'';//交易机构号
        $this->SubMerchantId         = $config['SubMerchantId']??''; //二级商户号

    }

    /**
     * 对参数urlencode并生成待签字符串
     * @param $params
     * @return string
     * @author 牛永光 nyg1991@aliyun.com
     * @date 2019/9/12 11:46
     */
    public function getSignContentUrlencode($params)
    {
        ksort($params);
        $stringToBeSigned = "";
        $i                = 0;
        foreach ($params as $k => $v) {
            if ("@" != substr($v, 0, 1)) {

                // 转换成目标字符集
                $v = $this->characet($v, $this->charset);

                if ($i == 0) {
                    $stringToBeSigned .= "$k" . "=" . urlencode($v);
                } else {
                    $stringToBeSigned .= "&" . "$k" . "=" . urlencode($v);
                }
                $i++;
            }
        }

        unset ($k, $v);
        return $stringToBeSigned;
    }

    /**
     * 生成签名
     * @param $data 待签名字符串
     * @return string
     * @author 牛永光 nyg1991@aliyun.com
     * @date 2019/9/21 16:39
     */
    public function sign($data)
    {
        if ($this->checkEmpty($this->rsaPrivateKeyFilePath)) {
            $priKey = $this->rsaPrivateKey;
            $res    = "-----BEGIN RSA PRIVATE KEY-----\n" .
                wordwrap($priKey, 64, "\n", true) .
                "\n-----END RSA PRIVATE KEY-----";
        } else {
            $priKey = file_get_contents($this->rsaPrivateKeyFilePath);
            $res    = openssl_get_privatekey($priKey, $this->pemPass);
        }

        ($res) or die('您使用的私钥格式错误，请检查RSA私钥配置');
        //SHA256withRSA签名
        openssl_sign($data, $sign, $res, OPENSSL_ALGO_SHA256);

        /*if (!$this->checkEmpty($this->rsaPrivateKeyFilePath)) {
            openssl_free_key($res);
        }*/

        return bin2hex($sign);
    }

    /**
     * 校验$value是否非空
     *  if not set ,return true;
     *    if is null , return true;
     **/
    protected function checkEmpty($value)
    {
        if (!isset($value))
            return true;
        if ($value === null)
            return true;
        if (trim($value) === "")
            return true;

        return false;
    }

    /**
     * 转换字符集编码
     * @param $data
     * @param $targetCharset
     * @return string
     */
    protected function characet($data, $targetCharset)
    {

        if (!empty($data)) {
            $fileType = $this->charset;
            if (strcasecmp($fileType, $targetCharset) != 0) {
                $data = mb_convert_encoding($data, $targetCharset, $fileType);
            }
        }


        return $data;
    }

    /**
     * 创建请求参数
     * @return array
     * @author 牛永光 nyg1991@aliyun.com
     * @date 2019/9/21 17:13
     */
    protected function creatParam()
    {
        $param = [
            'appCode'   => $this->appCode,
            'version'   => $this->version,
            'channelId' => $this->channelId,
            'charset'   => $this->charset,
            'algorithm' => $this->algorithm,
            'deviceId'  => $this->deviceId,
            'branchId'  => $this->branchId,
        ];
        //请求序列号【渠道端产生，一笔业务的唯一标识，贯穿整个交易始终】渠道代码（2位）+日期（yyMMdd 6位）+流水号（24位）
        $param['sequenceId'] = $this->channelId . date('ymd') . substr(md5(time() . rand(10000000, 99999999)), 0, 24);
        //交易时间戳:yyyyMMddHHmmssSSS
        list($msec, $sec) = explode(' ', microtime());
        $param['timestamp'] = date("YmdHis") . round(floatval($msec) * 1000, 0);
        return $param;
    }

    /**
     * 处理返回结果
     * @param $res
     * @return array
     * @author 牛永光 nyg1991@aliyun.com
     * @date 2019/9/23 17:01
     */
    protected function handle($res)
    {
       /* $sign_check = $this->checkResponseSign($res);
        if (!$sign_check) {
            return ['code' => -1, 'message' => '验签失败！'];
        }*/
        if ($res['code'] == '000000') {
            return ['code' => 0, 'message' => $res['message'], 'data' => json_decode($res['payload'], true)];
        }

        if($res['code'] == 'PAY8001')
        {
            $data                = json_decode($res['payload'],true);
            $data['TransStatus'] = '5';
            $data['TransTime']   = date('Y-m-d H:i:s',time());
            return ['code' => 0, 'message' => $res['message'], 'data' => $data];
        }

        return ['code' => -1, 'message' => $res['message']];
    }

    /**
     * curl请求
     * @param $headers header请求头
     * @param $param 参数（signature,payload）
     * @return bool|string json串
     * @throws Exception
     * @author 牛永光 nyg1991@aliyun.com
     * @date 2019/9/21 18:32
     */
    protected function curl($headers, $param)
    {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $this->gatewayUrl);
        curl_setopt($ch, CURLOPT_FAILONERROR, false);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);

        $postBodyString = "";
        $encodeArray    = Array();
        $postMultipart  = false;
        if (is_array($param) && 0 < count($param)) {

            foreach ($param as $k => $v) {
                if ("@" != substr($v, 0, 1)) //判断是不是文件上传
                {

                    $postBodyString  .= "$k=" . urlencode($this->characet($v, $this->charset)) . "&";
                    $encodeArray[$k] = $this->characet($v, $this->charset);
                } else //文件上传用multipart/form-data，否则用www-form-urlencoded
                {
                    $postMultipart   = true;
                    $encodeArray[$k] = new \CURLFile(substr($v, 1));
                }

            }
            unset ($k, $v);
            curl_setopt($ch, CURLOPT_POST, true);
            if ($postMultipart) {
                curl_setopt($ch, CURLOPT_POSTFIELDS, $encodeArray);
            } else {
                curl_setopt($ch, CURLOPT_POSTFIELDS, substr($postBodyString, 0, -1));
            }
        }

        if ($postMultipart) {
            $headers = array('content-type: multipart/form-data;charset=' . $this->charset . ';boundary=' . $this->getMillisecond());
        } else {
            $headers["Content-Type"] = 'application/x-www-form-urlencoded';
        }
        foreach ($headers as $k => $v) {
            $new_heasers[] = "{$k}:{$v}";
        }
        curl_setopt($ch, CURLOPT_HTTPHEADER, $new_heasers);
        $reponse = curl_exec($ch);
        if (curl_errno($ch)) {
            curl_close($ch);
            return json_encode(['code'=>'11111','message'=>'网络错误']);
        } else {
            $httpStatusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
            if (200 !== $httpStatusCode) {
                curl_close($ch);
                return json_encode(['code'=>'11111','message'=>'网络错误']);
            }
        }
        curl_close($ch);
        return $reponse;
    }

    /**
     * 拼接验签字段
     * @param $res
     * @return int
     * @author 牛永光 nyg1991@aliyun.com
     * @date 2019/9/23 16:59
     */
    public function checkResponseSign($res)
    {
        $plainMap = $res;
//        unset($plainMap['payload']);
        unset($plainMap['signature']);
//        $payload  = json_decode($res['payload'], true);
//        $plainMap = array_merge($plainMap, $payload);
        $str_sign = $this->getSignContentUrlencode($plainMap);
        return $this->rsaEncrypt($str_sign, $res['signature']);
    }

    /**
     * 验签
     * @param $original_str
     * @param $sign
     * @return int
     * @author 牛永光 nyg1991@aliyun.com
     * @date 2019/9/23 16:59
     */
    public function rsaEncrypt($original_str, $sign)
    {
        $pubKey = file_get_contents($this->rsaPublicKeyPath);

        $res    = openssl_get_publickey($pubKey);
        return openssl_verify($original_str, hex2bin($sign), $res, OPENSSL_ALGO_SHA256);
    }

    /**
     * 刷脸生成订单
     * @param $data
     * @return array
     * @throws Exception
     * @author 牛永光 nyg1991@aliyun.com
     * @date 2019/9/23 20:48
     */
    public function faceAddOrder($data)
    {
        $data['CurrencyCd']= 'CNY';
        $data['MerNbr']    = $this->MerNbr;
        $data['ChannelNbr']= '03';
        $data['PayTypCd'] = 'F';
        $data['PlatformDept'] = $this->PlatformDept;
        $data['OrderNbr']     = $data['MerSeqNbr'];

        $header              = $this->creatParam();
        $payload['MerNbr']   = $this->MerNbr;
        $payload             = array_merge($payload, $data);
        $sign_param          = array_merge($header, $payload);
        $sign                = $this->sign($this->getSignContentUrlencode($sign_param));
        $param['payload']    = json_encode($payload);
        $param['signature']  = $sign;
        $header['transCode'] = 'hnnx.trade.sandbox.upp.addorderinfo';
        $res                 = json_decode($this->curl($header, $param), true);
        return $this->handle($res);
    }

    /**
     * 刷脸生成订单
     * @param $data
     * @return array
     * @throws Exception
     * @author 牛永光 nyg1991@aliyun.com
     * @date 2019/9/23 20:48
     */
    public function facePay($data)
    {
        $data['MerNbr']    = $this->MerNbr;
        $data['OrderNbr']  = $data['MerSeqNbr'];
        $data['CurrencyCd']= 'CNY';
        $data['ChannelNbr']= '03';
        $data['AreaInfo']  = '5071';
        $data['PayTypCd'] = 'F';
        $header              = $this->creatParam();
        $payload['MerNbr']   = $this->MerNbr;
        $payload             = array_merge($payload, $data);
        $sign_param          = array_merge($header, $payload);
        $sign                = $this->sign($this->getSignContentUrlencode($sign_param));
        $param['payload']    = json_encode($payload);
        $param['signature']  = $sign;
        $header['transCode'] = 'hnnx.trade.sandbox.upp.merfacepay';

        $res                 = json_decode($this->curl($header, $param), true);
        return $this->handle($res);
    }

    /**
     * 鉴权
     * @param $data
     * @return array
     * @throws Exception
     * @author 牛永光 nyg1991@aliyun.com
     * @date 2019/9/23 20:48
     */
    public function infoIsPay($data)
    {
        $header              = $this->creatParam();
        $payload['MerNbr']   = $this->MerNbr;
        $payload             = array_merge($payload, $data);
        $sign_param          = array_merge($header, $payload);
        $sign                = $this->sign($this->getSignContentUrlencode($sign_param));
        $param['payload']    = json_encode($payload);
        $param['signature']  = $sign;
        $header['transCode'] = 'hnnx.trade.sandbox.upp.faceinfoispay';
        $res                 = json_decode($this->curl($header, $param), true);
        return $this->handle($res);
    }


    /**
     * 订单查询
     * @param $data
     * @return array
     * @throws Exception
     * @author 牛永光 nyg1991@aliyun.com
     * @date 2019/9/23 20:48
     */
    public function orderStatus($data)
    {
        $header              = $this->creatParam();
        $payload['MerNbr']   = $this->MerNbr;
        $payload             = array_merge($payload, $data);
        $sign_param          = array_merge($header, $payload);
        $sign                = $this->sign($this->getSignContentUrlencode($sign_param));
        $param['payload']    = json_encode($payload);
        $param['signature']  = $sign;
        $header['transCode'] = 'hnnx.trade.sandbox.upp.qryQrOrderStatus';
        $res                 = json_decode($this->curl($header, $param), true);
        return $this->handle($res);
    }
    
     //预支付
     public function prepay($data)
     {
         $header              = $this->creatParam();
         $payload['MerNbr']   = $this->MerNbr;
         $payload['SubMerchantId'] = $this->SubMerchantId;
         if($data['PayTypCd'] == "C"){
            //  $data['SubAppid'] = $this->SubAppid;//微信分配的子商户公众账号ID'wxee9d34876eea271e'
        }
         $payload             = array_merge($payload, $data);
         $sign_param          = array_merge($header, $payload);
         $sign                = $this->sign($this->getSignContentUrlencode($sign_param));
         $param['payload']    = json_encode($payload);
         $param['signature']  = $sign;
         $header['transCode'] = 'hnnx.trade.sandbox.upp.qrCodePrePay';
        //  Log::record($param,'hnnx_pay_param');
         trace('hnnx_pay_param'.json_encode($param));
         $res                 = json_decode($this->curl($header, $param), true);
        //  Log::record($res,'hnnx_pay_res');
         trace('hnnx_pay_res'.json_encode($res));
         return $this->handle($res);
     }

     /**
     * 用户主动扫码生成
     * @param $data
     * @return array
     * @throws Exception
     * @author 牛永光 nyg1991@aliyun.com
     * @date 2019/9/23 20:48
     */
    public function dyActiveQrCode($data)
    {
        $header              = $this->creatParam();
        $payload['MerNbr']        = $this->MerNbr;
        $payload['SubMerchantId'] = $this->SubMerchantId;
        $payload['MerNbr']   = $this->MerNbr;
        $payload             = array_merge($payload, $data);
        $sign_param          = array_merge($header, $payload);
        $sign                = $this->sign($this->getSignContentUrlencode($sign_param));
        $param['payload']    = json_encode($payload);
        $param['signature']  = $sign;
        $header['transCode'] = 'hnnx.trade.sandbox.upp.dyActiveQrCode';
        $res                 = json_decode($this->curl($header, $param), true);
        return $this->handle($res);
    }

    //退款
    public function tuikuan($data)
    {
        $header                   = $this->creatParam();
        $payload['MerNbr']        = $this->MerNbr;
        $payload['SubMerchantId'] = $this->SubMerchantId;
        $data['MerTransDateTime'] = date("YmdHis");
        $data['SubMerSeqNo']      = $data['MerSeqNbr'];
        $data['SubMerDateTime']   = $data['MerTransDateTime'];
        $data['OrderNbr']         = $data['SubMerSeqNo'];
        $data['OrigSubMerSeqNo']  = $data['OrigMerSeqNbr'];
        $data['OrigSubMerDate']   = $data['OrigMerDate'];
        $payload                  = array_merge($payload, $data);
        $sign_param               = array_merge($header, $payload);
        $sign                     = $this->sign($this->getSignContentUrlencode($sign_param));
        $param['payload']         = json_encode($payload);
        $param['signature']       = $sign;
        $header['transCode']      = 'hnnx.trade.sandbox.upp.returntrans';
        $res                      = json_decode($this->curl($header, $param), true);
        return $this->handle($res);
    }

    //对账查询
    public function accountqry($date)
    {
        $header               = $this->creatParam();
        $payload['ClearDate'] = $date;
        $sign_param           = array_merge($header, $payload);
        $sign                 = $this->sign($this->getSignContentUrlencode($sign_param));
        $param['payload']     = json_encode($payload);
        $param['signature']   = $sign;
        $header['transCode']  = 'hnnx.trade.sandbox.upp.accountqry';
        $res                  = json_decode($this->curl($header, $param), true);
        return $this->handle($res);
    }

    /**
     * 用户被扫支付
     * @param $data
     * @return array
     * @throws Exception
     * @author 牛永光 nyg1991@aliyun.com
     * @date 2019/9/23 20:34
     */
    public function coretoall($data)
    {
        $header              = $this->creatParam();
        // halt($header);
        $payload['MerNbr']   = $this->MerNbr;
        $payload['SubMerchantId']   = $this->SubMerchantId;
        $data['ChannelNbr']  = '03';
        $data['CurrencyCd']  = 'CNY';
        $data['AreaInfo']    = '4954';
        $payload             = array_merge($payload, $data);
        $sign_param          = array_merge($header, $payload);
        $sign                = $this->sign($this->getSignContentUrlencode($sign_param));
        $param['payload']    = json_encode($payload);
        $param['signature']  = $sign;
        $header['transCode'] = 'hnnx.trade.sandbox.upp.coretoall';
        trace('header'.json_encode($header));
        trace('param'.json_encode($param));
        // Log::write('header'.json_encode($header));
        // Log::write('param'.json_encode($param));
        $res                 = json_decode($this->curl($header, $param), true);
        trace('结果'.json_encode($res));
        // Log::write('结果'.json_encode($res));
        return $this->handle($res);
    }

    public function acountqry($data)
    {
        $header              = $this->creatParam();
        $payload             = $data;
        $sign_param          = array_merge($header, $payload);
        $sign                = $this->sign($this->getSignContentUrlencode($sign_param));
        $param['payload']    = json_encode($payload);
        $param['signature']  = $sign;
        $header['transCode'] = 'hnnx.trade.sandbox.upp.accountqry';
        $res                 = json_decode($this->curl($header, $param), true);
        halt($res);
        return $this->handle($res);
    }

}
